import math
from ClipperAdaptor import ClipperAdaptor
from GeomAlgo import intersect
import pyclipper
import numpy as np
from GeomBase import Matrix3D, Point3D, Vector3D
from Line import Line
from Polyline import Polyline
from Segment import Segment


class SweepLine:
    def __init__(self, segs=[]):
        self.segs = segs

    def intersect(self, y):
        ips = []
        yLine = Line(Point3D(0, y, self.segs[0].A.z), Vector3D(1, 0, 0))
        for seg in self.segs:
            if seg.A.y == y:
                ips.append(seg.A.clone())
            elif seg.B.y == y:
                ips.append(seg.B.clone())
            else:
                ip = intersect(yLine, seg)
                if ip:
                    ips.append(ip)
        ips.sort(key=lambda p: p.x)
        i = len(ips) - 1
        while i > 0:
            if ips[i].distanceSquare(ips[i - 1]) == 0:
                del ips[i], ips[i - 1]
                i -= 2
            else:
                i -= 1
        return ips


def calcHatchPoints(polygons, ys):
    segs = []
    for poly in polygons:
        for i in range(poly.count() - 1):
            seg = Segment(poly.points[i], poly.points[i + 1])
            seg.yMin = min(seg.A.y, seg.B.y)
            seg.yMax = max(seg.A.y, seg.B.y)
            segs.append(seg)
    segs.sort(key=lambda s: s.yMin)
    k, sweep = 0, SweepLine()
    ipses = []
    for y in ys:
        for i in range(len(sweep.segs) - 1, -1, -1):
            if sweep.segs[i].yMax < y:
                del sweep.segs[i]
        for i in range(k, len(segs)):
            if segs[i].yMin < y and segs[i].yMax >= y:
                sweep.segs.append(segs[i])
            elif segs[i].yMin >= y:
                k = i
                break
        if len(sweep.segs) > 0:
            ips = sweep.intersect(y)
            ipses.append(ips)
    return ipses


def genHatches(polygons, ys):
    segs = []
    ipses = calcHatchPoints(polygons, ys)
    for ips in ipses:
        for i in range(0, len(ips) - 1, 2):
            seg = Segment(ips[i], ips[i + 1])
            segs.append(seg)
    return segs


def genSweepHatches(polygons, interval, angle):
    mt = Matrix3D.createRotateMatrix("Z", -angle)
    mb = Matrix3D.createRotateMatrix("Z", angle)
    rotPolys = []
    for poly in polygons:
        rotPolys.append(poly.multiplied(mt))
    yMin, yMax = float("inf"), float("-inf")
    for poly in rotPolys:
        for pt in poly.points:
            yMin, yMax = min(yMin, pt.y), max(yMax, pt.y)
    y = yMin + interval
    ys = [t for t in np.arange(y, yMax, interval)]
    segs = genHatches(rotPolys, ys)
    for seg in segs:
        seg.multiply(mb)
    return segs


def genClipHatches(polygons, interval, angle):
    xMin, xMax = float("inf"), float("-inf")
    yMin, yMax = float("inf"), float("-inf")
    z = polygons[0].points[0].z
    for poly in polygons:
        for pt in poly.points:
            xMin, xMax = min(xMin, pt.x), max(xMax, pt.x)
            yMin, yMax = min(yMin, pt.y), max(yMax, pt.y)
    v = Vector3D(math.cos(angle), math.sin(angle), z)
    n = Vector3D(math.cos(angle + math.pi / 2), math.sin(angle + math.pi / 2), z)
    O = Point3D((xMax + xMin) / 2, (yMin + yMax) / 2, z)
    R = math.sqrt((xMax - xMin) ** 2 + (yMax - yMin) ** 2) / 2
    P1 = O - n.amplified(R)
    parallels = []
    for i in range(0, int(2 * R / interval) + 1, 1):
        Q = P1 + n.amplified(interval * i)
        seg = Polyline()
        seg.addPoint(Q - v.amplified(R))
        seg.addPoint(Q + v.amplified(R))
        parallels.append(seg)
    hatchSegs = []
    ca = ClipperAdaptor()
    clipper = pyclipper.Pyclipper()
    clipper.AddPaths(ca.toPaths(polygons), pyclipper.PT_CLIP, True)
    clipper.AddPaths(ca.toPaths(parallels), pyclipper.PT_SUBJECT, False)
    sln = clipper.Execute2(pyclipper.CT_INTERSECTION)
    for child in sln.Childs:
        if len(child.Contour) > 0:
            poly = ca.toPoly(child.Contour, z, False)
            seg = Segment(poly.startPoint(), poly.endPoint())
            hatchSegs.append(seg)
    return hatchSegs
